#!/usr/bin/env python3
import os
import sys
import random
import string

CURDIR = os.path.dirname(os.path.realpath(__file__))
sys.path.insert(0, os.path.join(CURDIR, 'helpers'))

from pure_http_client import ClickHouseClient

def get_random_string(length):
    return ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(length))

client = ClickHouseClient()

def insert_block(table_name, block_granularity_rows, block_rows):
    global client
    block_data = []
    index_granularity_bytes = 10 * 1024 * 1024
    row_bytes = index_granularity_bytes // block_granularity_rows
    for _ in range(block_rows):
        block_data.append(get_random_string(row_bytes - 1))

    values_row = ", ".join("(1, '" + row + "')" for row in block_data)
    client.query("INSERT INTO {} VALUES {}".format(table_name, values_row))

try:
    client.query("DROP TABLE IF EXISTS t")
    client.query("CREATE TABLE t (v UInt8, data String) ENGINE = MergeTree() ORDER BY tuple() SETTINGS min_bytes_for_wide_part = 0")

    client.query("SYSTEM STOP MERGES t")

    # These blocks size taken from the real table which reproduces the error
    # when we get granule with more rows then fixed granularity after horizontal merge.
    # About 10k rows when max is 8912.
    #
    # Why these blocks are special?
    # 1) The first one contains 1811 rows, but its granule should have 6853.
    # So we write 1811 and get unfinished granule with 6853 - 1811 = 5042 rows to write from the next blocks.
    #
    # 2) The second block has fewer rows than rows left in the unfinished granule (3094 < 5042).
    # It can be written entirely in this unfinished granule and we will still have some rows left. But it's granularity
    # should be smaller than rows left in granule (3094 < 5042), so clickhouse will adjust (make smaller) this last unfinished granule.
    # This adjust logic contained a bug: we adjust not to the new block's granularity (3094), but to the difference of the new block granularity and
    # already written rows (3094 - 1811 = 1283). This lead to several unsigned integer overflows in code and huge granules as result.
    #
    # 3) Last block just triggers the check that each granule has fewer rows than fixed granularity rows. If the bug from 2) exists then it will fail.
    insert_block("t", block_granularity_rows=6853, block_rows=1811)
    insert_block("t", block_granularity_rows=3094, block_rows=3094)
    insert_block("t", block_granularity_rows=6092, block_rows=6092)

    client.query("SYSTEM START MERGES t")
    client.query("OPTIMIZE TABLE t FINAL")

    print(client.query_return_df("SELECT COUNT() as C FROM t FORMAT TabSeparatedWithNames")['C'][0])
finally:
    client.query("DROP TABLE IF EXISTS t")
